package logic

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/redis/go-redis/v9"
	"github.com/we7coreteam/w7-rangine-go-support/src/facade"
	"log"
	"os"
	"runtime"
	"time"
)

func NewCollection() *Collection {
	hostName, _ := os.Hostname()
	server := &Collection{}
	server.ConnList = make(map[string]*Client)
	server.trashList = make(map[string]*Client)
	server.register = make(chan *Client)
	server.destroy = make(chan *Client)
	server.pullMsgQueue = make(chan string)
	server.Context, server.CancelFunc = context.WithCancel(context.Background())
	server.redis, _ = facade.GetRedisFactory().Channel("default")
	server.ServerHostName = hostName
	return server
}

type Collection struct {
	ConnList       map[string]*Client
	trashList      map[string]*Client // 垃圾连接，
	register       chan *Client       // 用于注册连接
	destroy        chan *Client       // 用于断开连接
	pullMsgQueue   chan string        // 用于接收推送队列消息
	Context        context.Context
	CancelFunc     context.CancelFunc
	Len            int
	redis          redis.Cmdable
	ServerHostName string
}

func (self Collection) Register(client *Client) {
	self.register <- client
}

func (self Collection) Destroy(client *Client) {
	self.destroy <- client
}

func (self Collection) Pull(message string) {
	self.pullMsgQueue <- message
}

func (self Collection) Loop() {
	queuePullMessage := fmt.Sprintf(QUEUE_PULL_MESSAGE, facade.GetConfig().GetString("setting.queue"))
	go self.recycle()
	for {
		select {
		case client := <-self.register:
			if oldClient, ok := self.ConnList[client.Fd]; ok {
				// 用户二次连接后，需要将前一个连接断开，
				// 断开连接时，需要先通知前端主动断开，
				// 后端将垃圾连接放到回收站中，隔一段时间前端如果没有主动断开，再主动断
				oldClient.IsDelete = true
				oldClient.DeleteTime = time.Now().Unix()
				self.trashList[oldClient.Id] = oldClient
				oldClient.SendMessageQueue <- fmt.Sprintf("{\"type\":\"repeat_login\",\"fd_info\":\"%s\"}", oldClient.Id)

				self.ConnList[client.Fd] = client
			} else {
				self.ConnList[client.Fd] = client
				self.Len++
				content, _ := json.Marshal(ContentMessage{
					Token: client.Token,
					Type:  MESSAGE_TYPE_JOIN,
				})
				message, _ := json.Marshal(PushMessage{
					Fd:      client.Fd,
					Content: string(content),
				})
				log.Println("注册事件:", string(message))
				self.redis.RPush(self.Context, queuePullMessage, message)
			}
		case client := <-self.destroy:
			if currentClient, ok := self.ConnList[client.Fd]; ok {
				if client.Id == currentClient.Id {
					delete(self.ConnList, client.Fd)
					self.Len--
					content, _ := json.Marshal(ContentMessage{
						Type: MESSAGE_TYPE_QUIT,
					})
					message, _ := json.Marshal(PushMessage{
						Fd:      client.Fd,
						Content: string(content),
					})
					log.Println("断开连接:", string(message))
					self.redis.RPush(self.Context, queuePullMessage, message)
					// 主动断开后，不必放到垃圾回收中，直接关闭连接
					client.Close()
				}
			}
			// 查看是否是已经回收的连接
			if currentClient, ok := self.trashList[client.Id]; ok {
				delete(self.trashList, client.Id)
				currentClient.Close()
			}
		case message := <-self.pullMsgQueue:
			log.Println("推送消息:", string(message))
			self.redis.RPush(self.Context, queuePullMessage, message)
		default:
			redis, _ := facade.GetRedisFactory().Channel("default")
			hostName, _ := os.Hostname()
			value := redis.LPop(self.Context, fmt.Sprintf(QUEUE_PUSH_MESSAGE, hostName))
			if value.Err() == nil {
				recvPushMessage := PushMessage{}
				json.Unmarshal([]byte(value.Val()), &recvPushMessage)
				log.Printf("监听队列，收到消息: %s, %+v \n", fmt.Sprintf(QUEUE_PUSH_MESSAGE, hostName), recvPushMessage)
				if client, ok := self.ConnList[recvPushMessage.Fd]; ok {
					client.SendMessageQueue <- recvPushMessage.Content
				} else {
					log.Printf(" %s 未联接 %+v \n", recvPushMessage.Fd, self.ConnList)
				}
			}
		}
	}
}

func (self Collection) recycle() {
	connListLen := len(self.ConnList)
	trashListLen := len(self.trashList)

	for true {
		// 检测回收站连接
		for key, value := range self.trashList {
			if time.Now().Unix()-value.DeleteTime > 60 {
				value.Close()
				delete(self.trashList, key)
			}
		}

		if len(self.ConnList) != connListLen || len(self.trashList) != trashListLen {
			connListLen = len(self.ConnList)
			trashListLen = len(self.trashList)
			log.Printf("连接用户数: %d，垃圾用户数：%d，协程数：%d \n", connListLen, trashListLen, runtime.NumGoroutine())
		}
		time.Sleep(time.Second)
	}
}
